#ident	"@(#)Application.cc	2.1	AKK	20110303"
/** @file Application.cc
 * De implementatie van Application.
 */

// Unix/Linux includes
#include <cstdlib>			// srand(3), rand(3), exit(2)

// Our own includes
#include "ansi.h"			// ansi color codes
#include "main.h"			// common global stuff
#include "Stopwatch.h"		// De cpu tijd meter
#include "Application.h"	// De pseudo applicatie
#include "Fitter.h"
#include <vector>


// introduce std shorthands
using std::cout;
using std::cerr;
using std::endl;

// ===================================================================

/// Een, globale, hulp functie die de kans berekent
/// dat de applicatie geheugenruimte wil aanvragen.
inline // deze 'inline' is alleen maar wat extra optimalisatie
bool	vraagkans(int r)
{
	return (((r >> 5) % 16) < 7);
	// De "vraag" kans is 7 op 16.
	// N.B. Bij veel 'random' generatoren zijn de laatste
	// bitjes niet even random als de rest. Daarom kijken
	// we hier naar een paar bitjes "halverwege" de
	// gegeven 'r' en niet naar de "laatste" bitjes.
	//		>> = schuif bitjes naar rechts
	//		%  = modulo = rest na deling
}

// ------------------------------------------------------


// Initieer een "Application" die geheugen vraagt aan
// de gegeven beheerder, waarbij we de beschikking
// hebben over 'size' eenheden geheugen.
Application::Application(Allocator *beheerder, int size)
	: beheerder(beheerder), size(size)
	, vflag(false), tflag(true)
	, err_teller(0), oom_teller(0)
{
	// nooit iets geloven ...
	require(beheerder != 0);
	require(size > 0);
}


// actie: na afloop alles netjes opruimen.
Application::~Application()
{
	vflag = false; // svp het opruimen in stilte doen
	// zijn er nog objecten over?
	while (!objecten.empty()) {
		vergeetOudste();
	}
}


// actie: vraag om geheugen (onze versie van 'new')
void	Application::vraagGeheugen(int omvang)
{
	if (vflag) {
		cout << "Vraag " << omvang << ", ";
	}

	// Deze interne controle overslaan als we aan het testen zijn.
	if (!tflag) {
		require((0 < omvang) && (omvang <= size));	// is de 'omvang' wel geldig ?
	}

	// Vraag om geheugen
	Area  *ap = beheerder->alloc(omvang);
	if (ap == 0) {  // Allocator out of memory?
		if (vflag) {
			cout << "out of memory" << endl;
		}
		++oom_teller;	// out-of-memory teller bijwerken
		return;
	}
	if (vflag) {
		cout << "kreeg " << (*ap) << endl;
	}

	// Nu moeten we eerst controlen of er geen overlap
	// bestaat met wat we al eerder hadden gekregen ...
	ALiterator  i;
	for (i = objecten.begin() ; i != objecten.end() ; ++i) {
		Area  *xp = *i;				// m.a.w. objecten[ i ]
		if (ap->overlaps(xp)) {     // Oeps!
			// Dit zou eigenlijk een "fatal error" moeten zijn,
			// maar bij de RandomFit zal dit wel vaker gebeuren
			// dus voorlopig alleen maar melden dat het fout is ...
			if (vflag) {
				cout << "Oeps, het nieuwe gebied overlapt met " << (*xp) << endl;
			}
			++err_teller;	// fouten teller bijwerken
			break;			// verder zoeken is niet meer zo zinvol ...
							// ... en levert alleen maar meer uitvoer op.
		}
	}

	// Het gekregen gebied moeten we natuurlijk wel onthouden.
	objecten.push_back(ap);
}


// actie: geef een gekregen gebied weer terug (onze versie van 'delete')
void	Application::vergeetOudste()
{
	require(! objecten.empty());	// hebben we eigenlijk wel wat ?
	Area  *ap = objecten.front();	// het oudste gebied opzoeken
	if (vflag) {
		// vertel wat we gaan doen
		cout << "Vrijgeven " << (*ap) << endl;
	}
	objecten.pop_front();			// gebied uit de lijst halen
	beheerder->free(ap);			// en vrij geven
}


// Utility:
// Returns a random integer in the range
// from min (inclusive) upto max (exclusive)
inline
int randint(int min, int max)
{
	int  m = (max - min);	// bepaal bereik
	int  r = rand();
	r %= m;					// rest na deling
	return r + min;
}


// actie: geef een gekregen gebied weer terug (onze versie van 'delete')
void	Application::vergeetRandom()
{
	require(! objecten.empty());	// hebben we eigenlijk wel wat ?

	Area  *ap = objecten.front();	// het oudste gebied alvast opzoeken

	int  n = objecten.size();		// valt er wat te kiezen?
	if (n > 1) {
		int  m = randint(0, n);		// kies een index
		// en zoek dat element op
		ALiterator  i;
		for (i = objecten.begin() ; (m > 0) && (i != objecten.end()) ; ++i, --m) {
			;
		}
		ap = *i;					// het slachtoffer
		objecten.erase(i);			// uit de lijst halen
	} else {
		objecten.pop_front();    // oudste gebied uit de lijst halen
	}

	if (vflag) {
		// vertel wat we gaan doen
		cout << "Vrijgeven " << (*ap) << endl;
	}

	beheerder->free(ap);			// en het gebied weer vrij geven
}


// =========== BEHEER MODULE METEN =============

// Deze methode doet "zomaar" wat onzin acties.
// Bedenk zelf een andere methode die wat zinnigers doet.
void	Application::randomscenario(int aantal, bool vflag)
{
	bool old_vflag = this->vflag;
	this->vflag = vflag;	// verbose mode aan/uit

	oom_teller = 0;			// reset failure counter
	err_teller = 0;			// reset error counter

	// Door srand hier aan te roepen met een "seed" waarde
	// krijg je altijd een herhaling van hetzelfde scenario.
	// Je kan elke seed waarde dan zien als de code voor "een scenario".
	// Handig voor het testen/meten, maar bedenk wel dat deze scenario's
	// nooit gelijkwaardig zijn aan het gedrag van een echt systeem.
	srand(1);   // (zie: man 3 rand)

	// Nu komt het eigenlijke werk:
	Stopwatch  klok;		// Een stopwatch om de tijd te meten
	klok.start();			// -----------------------------------
	for (int  x = 0 ; x < aantal ; ++x) {	// Doe nu tig-keer "iets".
		int  r = rand();					// Gooi de dobbelsteen
		if (objecten.empty()				// Als we nog niets hebben of
		  || vraagkans(r) )					// we kiezen voor ruimte aanvragen
		{
			r = rand();						// Gooi de dobbelsteen nog eens
			r %= (size / 100);				// maximaal 1% van alles
			vraagGeheugen(r + 1);			// maar minstens 1 eenheid
		} else								// Anders: vrijgeven mits ...
		if (!objecten.empty()) {			// ... we iets hebben
			//vergeetOudste();				// geef het oudste gebied weer terug
			vergeetRandom();				// geef een gebied weer terug
		}
		// else
			// dan doen we een keer niets
	}
	klok.stop();			// -----------------------------------

	klok.report();			// Vertel alle tijden
	beheerder->report();	// en de geheugenbeheer statistieken
	// Evaluatie
	if ((oom_teller > 0) || (err_teller > 0) ) {	// some errors
	    cout <<  "De allocater faalde " << oom_teller << " keer";
	    cout << " en maakte " << err_teller << " fouten\n" ;
	} else {										// no problems
	    cout <<  "De allocater faalde " << oom_teller << " keer";
	    cout << " en maakte " << err_teller << " fouten\n" ;
	}

	int areaSize = ((Fitter *)beheerder)->areas.size();
	double score = klok.gettotal()* areaSize;

	cout << "---------------------------" << endl;
	cout << "Score forumle" << endl;
	cout << klok.gettotal()<< " * " << areaSize<< " = " << score << endl;

	vflag = old_vflag; // turn on verbose output again
}


// =========== BEHEER MODULE TESTEN ==========

// Elke test die hieronder uitgevoerd wordt zou een 'assert' failure
// moeten veroorzaken die de normale executie-volgorde afbreekt.

void	Application::testing()
{
	Area  *ap = 0;

	tflag = true;		// Zet de sanity-check in 'vraagGeheugen' even uit.

	err_teller = 0;		// Reset the Error teller.
	oom_teller = 0;		// Reset the Out-Of-Memory teller.

	int	 failed_steps = 0;
	int  fase = 0;		// Houdt bij hoe ver we zijn in de test procedure.

	// Na elke 'test' komen we weer "hier" terug voor de volgende test.
	while (fase >= 0)
	{
		try
		{
			cerr << "Application Stap " << (++fase) << ":\n";
			switch (fase)
			{
				case 1:
					cerr << "Om niets vragen ...\n";
					vraagGeheugen(0);   // dit mag niet kunnen ...
					// ... dus hier zouden we niet mogen komen ...
					cerr << "FAILED" << "\n"; // ... maar de controle heeft gefaald
					++failed_steps;
					//cerr << "Stap " << (++fase) << ":\n";
					/*FALLTHRU*/						// ga door naar de volgende test
					break;
				case 2:
					cerr << "Te veel vragen ...\n";
					vraagGeheugen(size + 1);
					cerr << "FAILED" << "\n"; // controle heeft gefaald
					++failed_steps;
					//cerr << "Stap " << (++fase) << ":\n";
					/*FALLTHRU*/						// ga door naar de volgende test
					break;
				case 3:
					cerr << "Te weinig vragen ...\n";
					vraagGeheugen(-1);
					cerr << "FAILED"<< "\n";
					++failed_steps;
					//cerr << "Stap " << (++fase) << ":\n";
					/*FALLTHRU*/
					break;
				case 4:
					cerr << "Om alles vragen ...\n";
					++failed_steps;
					vraagGeheugen(size);   			// PAS OP: dit mag dus wel!
					--failed_steps;
					cerr << "OKE" << "\n";
					//cerr << "Stap " << (++fase) << ":\n";
					/*FALLTHRU*/
					break;
				case 5:
					cerr << "Ruimte weer vrijgeven ...\n";
					++failed_steps;
					vergeetOudste(); 		// geheugen van stap 4 weer vrijgeven
					--failed_steps;
					cerr << "OKE" << "\n"; // Zou geen problemen mogen geven
					//cerr << "Stap " << (++fase) << ":\n";
					/*FALLTHRU*/
					break;
				case 6:
					cerr << "Een gebied twee keer vrijgeven ...\n";
					// Vraag om geheugen
					ap = beheerder->alloc(size / 2);	// dit moet altijd kunnen
					beheerder->free(ap);				// weer vrij geven
					beheerder->free(ap);				// en nog eens vrij geven
					cerr << "FAILED""\n";
					++failed_steps;
					//cerr << "Stap " << ( ++fase ) << ":\n";
					/*FALLTHRU*/
					// Voeg zonodig nog andere testcases toe
					break;
				default:
					if (failed_steps > 0)
						cerr << "Einde code testen " <<"with "<<failed_steps<<" errors""\n";
					else
						cerr << "Einde code testen, alles OKE""\n";
					fase = -1;		// einde test loop
					tflag = false;	// Puur "voor het geval dat"
					break;
			}
		} catch (const std::logic_error& error) {
			cerr << error.what();
			cerr << "OKE""\n";
		}
	}
}


void Application::ReeksTestApplicationScenarion(bool vflag, int seed)
{
	int aantal = 500;
	vector<Score *> scores;

	int loops = 10; // aantal loops dat gedaan word voor het gemiddelde
	int increase = 10;
	int hoeveel = 500;

	for(int t=0; t<loops; t++)    //This loops on the rows.
	{
		for(int i=0; i<increase; i++)    //This loops on the rows.
		{
			//cout << i << endl;
			Score * score =  TestApplicationScenarion(aantal, vflag, seed);
			aantal = aantal + hoeveel;

			if(t ==0 )
				scores.push_back(score);
			else
			{
				scores[i]->errors += score->errors;
				scores[i]->fails += score->fails;
				scores[i]->score += score->score;
			}
		}
		seed++;
	}
	for(int i=0; i<increase; i++)    //This loops on the rows.
	{

		scores[i]->errors = (scores[i]->errors / loops);
		scores[i]->fails = (scores[i]->fails / loops);
		scores[i]->score = (scores[i]->score / loops);

		cout << scores[i]->size << ":" << scores[i]->times << "  score: "
				<< scores[i]->score << "  fouten: " << scores[i]->errors
				<< " fails: " << scores[i]->fails << endl;
		//cout << scores[i]->score  << endl;
	}
}

Score *  Application::TestApplicationScenarion(int aantal, bool vflag, int seed)
{

	Score * score;

	bool old_vflag = this->vflag;
	this->vflag = vflag;	// verbose mode aan/uit

	oom_teller = 0;			// reset failure counter
	err_teller = 0;			// reset error counter

	// Door srand hier aan te roepen met een "seed" waarde
	// krijg je altijd een herhaling van hetzelfde scenario.
	// Je kan elke seed waarde dan zien als de code voor "een scenario".
	// Handig voor het testen/meten, maar bedenk wel dat deze scenario's
	// nooit gelijkwaardig zijn aan het gedrag van een echt systeem.
	srand(seed);   // (zie: man 3 rand)

	// Nu komt het eigenlijke werk:
	Stopwatch  klok;		// Een stopwatch om de tijd te meten
	klok.start();			// -----------------------------------
	for (int  x = 0 ; x < aantal ; ++x) {	// Doe nu tig-keer "iets".

		int  r = rand();					// Gooi de dobbelsteen
		if (objecten.empty()				// Als we nog niets hebben of
		  || vraagkans(r) )					// we kiezen voor ruimte aanvragen
		{
			r = rand();						// Gooi de dobbelsteen nog eens
			r %= (10 * (size / 100));		// maximaal 30% van alles
			vraagGeheugen(r + 1);

		} else								// Anders: vrijgeven mits ...
		if (!objecten.empty()) {			// ... we iets hebben
			//vergeetOudste();				// geef het oudste gebied weer terug
			vergeetRandom();				// geef een gebied weer terug
		}

	}
	klok.stop();			// -----------------------------------

	//bereken score
	int areaSize = ((Fitter *)beheerder)->areas.size();
	double score2 = (klok.gettotal()* areaSize ) * ((oom_teller/aantal) +1);

	score = new Score(oom_teller, err_teller, size, aantal, score2);

	if(this->vflag){
		klok.report();			// Vertel alle tijden
		beheerder->report();	// en de geheugenbeheer statistieken
		// Evaluatie
		if ((oom_teller > 0) || (err_teller > 0) ) {	// some errors
			cout <<  "De allocater faalde " << oom_teller << " keer";
			cout << " en maakte " << err_teller << " fouten\n" ;
		}
		cout << klok.gettotal()<< " * " << areaSize<< " = " << score2 << endl;
	}

	vflag = old_vflag; // turn on verbose output again

	return score;
}

// vim:sw=4:ai:aw:ts=4:
